﻿namespace S7.Net
{
    using S7.Net.Types;
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Linq;
    using System.Net;
    using System.Net.Sockets;

    public class Plc : IDisposable
    {
        private Socket _mSocket;
        private const int CONNECTION_TIMED_OUT_ERROR_CODE = 0x274c;
        private bool disposedValue;

        private byte[] plcHead1 = new byte[]
        {
            3, 0, 0, 22, 17, 224, 0, 0, 0, 1, 0, 192, 1, 10, 193, 2, 1, 2, 194, 2, 1, 0
        };

        private byte[] plcHead2 = new byte[]
        {
            3, 0, 0, 25, 2, 240, 128, 50, 1, 0, 0, 4, 0, 0, 8, 0, 0, 240, 0, 0, 1, 0, 1, 1, 224
        };

        private byte[] plcOrderNumber = new byte[]
        {
            3, 0, 0, 33, 2, 240, 128, 50, 7, 0, 0, 0, 1, 0, 8, 0, 8, 0, 1, 18, 4, 17, 68, 1, 0, 255, 9, 0, 4, 0, 17, 0, 0
        };

        private byte[] plcHead1_200 = new byte[]
        {
            3, 0, 0, 22, 17, 224, 0, 0, 0, 1, 0, 193, 2, 77, 87, 194, 2, 77, 87, 192, 1, 9
        };

        private byte[] plcHead2_200 = new byte[]
        {
            3, 0, 0, 25, 2, 240, 128, 50, 1, 0, 0, 0, 0, 0, 8, 0, 0, 240, 0, 0, 1, 0, 1, 3, 192
        };

        private byte[] plcHead1_200smart = new byte[]
        {
            3, 0, 0, 22, 17, 224, 0, 0, 0, 1, 0, 193, 2, 16, 0, 194, 2, 3, 0, 192, 1, 10
        };

        private byte[] plcHead2_200smart = new byte[]
        {
            3, 0, 0, 25, 2, 240, 128, 50, 1, 0, 0, 204, 193, 0, 8, 0, 0, 240, 0, 0, 1, 0, 1, 3, 192
        };

        private byte[] S7_STOP = new byte[]
        {
            3,0,0,33,2,240,128,50,1,0, 0, 14, 0, 0, 16, 0, 0, 41, 0, 0, 0, 0, 0, 9, 80, 95, 80, 82, 79, 71, 82, 65, 77
        };

        private byte[] S7_HOT_START = new byte[]
        {
            3, 0, 0, 37, 2, 240, 128, 50, 1, 0, 0, 12, 0, 0, 20, 0, 0, 40, 0, 0, 0, 0, 0, 0, 253, 0, 0, 9, 80, 95, 80, 82, 79, 71, 82, 65, 77
        };

        private byte[] S7_COLD_START = new byte[]
        {
            3, 0, 0, 39, 2, 240, 128, 50, 1, 0, 0, 15, 0, 0, 22, 0, 0, 40, 0, 0, 0, 0, 0, 0, 253, 0, 2, 67, 32, 9, 80, 95, 80, 82, 79, 71, 82, 65, 77
        };

        public Plc(CpuType cpu, string ip, short rack, short slot)
        {
            if (!System.Enum.IsDefined(typeof(CpuType), cpu))
            {
                throw new InvalidEnumArgumentException("cpu", (int) cpu, typeof(CpuType));
            }
            if (string.IsNullOrEmpty(ip))
            {
                throw new ArgumentException("IP address must valid.", "ip");
            }
            this.CPU = cpu;
            this.IP = ip;
            this.Rack = rack;
            this.Slot = slot;
            this.MaxPDUSize = 240;
        }

        public void ClearLastError()
        {
            this.LastErrorCode = S7.Net.ErrorCode.NoError;
            this.LastErrorString = string.Empty;
        }

        public void Close()
        {
            if ((this._mSocket != null) && this._mSocket.Connected)
            {
                this._mSocket.Shutdown(SocketShutdown.Both);
                this._mSocket.Close();
            }
        }

        private S7.Net.ErrorCode Connect(Socket socket)
        {
            try
            {
                IPEndPoint remoteEP = new IPEndPoint(IPAddress.Parse(this.IP), 0x66);
                socket.Connect(remoteEP);
                return S7.Net.ErrorCode.NoError;
            }
            catch (SocketException exception)
            {
                if (exception.SocketErrorCode == SocketError.TimedOut)
                {
                    this.LastErrorCode = S7.Net.ErrorCode.IPAddressNotAvailable;
                }
                else
                {
                    this.LastErrorCode = S7.Net.ErrorCode.ConnectionError;
                }
                this.LastErrorString = exception.Message;
            }
            catch (Exception exception2)
            {
                this.LastErrorCode = S7.Net.ErrorCode.ConnectionError;
                this.LastErrorString = exception2.Message;
            }
            return this.LastErrorCode;
        }

        private ByteArray CreateReadDataRequestPackage(DataType dataType, int db, int startByteAdr, int count = 1)
        {
            ByteArray array = new ByteArray(12);
            array.Add(new byte[] { 0x12, 10, 0x10 });
            if ((dataType - 0x1c) <= ((DataType) 1))
            {
                array.Add((byte) dataType);
            }
            else
            {
                array.Add((byte) 2);
            }
            array.Add(Word.ToByteArray((ushort) count));
            array.Add(Word.ToByteArray((ushort) db));
            array.Add((byte) dataType);
            int num = (int) (((long) (startByteAdr * 8)) / 0xffff);
            array.Add((byte) num);
            if ((dataType - 0x1c) <= ((DataType) 1))
            {
                array.Add(Word.ToByteArray((ushort) startByteAdr));
                return array;
            }
            array.Add(Word.ToByteArray((ushort) (startByteAdr * 8)));
            return array;
        }

        public void Dispose()
        {
            this.Dispose(true);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (!this.disposedValue)
            {
                if ((disposing && (this._mSocket != null)) && this._mSocket.Connected)
                {
                    this._mSocket.Shutdown(SocketShutdown.Both);
                    this._mSocket.Close();
                }
                this.disposedValue = true;
            }
        }

        public S7.Net.ErrorCode Open()
        {
            this._mSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            this._mSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, 0x3e8);
            this._mSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.SendTimeout, 0x3e8);
            if (this.Connect(this._mSocket) != S7.Net.ErrorCode.NoError)
            {
                return this.LastErrorCode;
            }
            try
            {
                byte[] buffer = new byte[] { 
                    3, 0, 0, 0x16, 0x11, 0xe0, 0, 0, 0, 0x2e, 0, 0xc1, 2, 1, 0, 0xc2, 
                    2, 3, 0, 0xc0, 1, 9
                 };
                switch (this.CPU)
                {
                    case CpuType.S7400:
                        buffer[11] = 0xc1;
                        buffer[12] = 2;
                        buffer[13] = 1;
                        buffer[14] = 0;
                        buffer[15] = 0xc2;
                        buffer[0x10] = 2;
                        buffer[0x11] = 3;
                        buffer[0x12] = (byte) (((this.Rack * 2) * 0x10) + this.Slot);
                        break;

                    case CpuType.S7200Smart:
                    case CpuType.S71200:
                    case CpuType.S7300:
                        buffer[11] = 0xc1;
                        buffer[12] = 2;
                        buffer[13] = 1;
                        buffer[14] = 0;
                        buffer[15] = 0xc2;
                        buffer[0x10] = 2;
                        buffer[0x11] = 3;
                        buffer[0x12] = (byte) (((this.Rack * 2) * 0x10) + this.Slot);
                        break;

                    case CpuType.S71500:
                        buffer[11] = 0xc1;
                        buffer[12] = 2;
                        buffer[13] = 0x10;
                        buffer[14] = 2;
                        buffer[15] = 0xc2;
                        buffer[0x10] = 2;
                        buffer[0x11] = 3;
                        buffer[0x12] = (byte) (((this.Rack * 2) * 0x10) + this.Slot);
                        break;

                    case CpuType.S7200:
                        buffer[11] = 0xc1;
                        buffer[12] = 2;
                        buffer[13] = 0x10;
                        buffer[14] = 0;
                        buffer[15] = 0xc2;
                        buffer[0x10] = 2;
                        buffer[0x11] = 0x10;
                        buffer[0x12] = 0;
                        break;

                    default:
                        return S7.Net.ErrorCode.WrongCPU_Type;
                }
                this._mSocket.Send(buffer, 0x16, SocketFlags.None);
                byte[] buffer2 = new byte[] { 
                    3, 0, 0, 0x19, 2, 240, 0x80, 50, 1, 0, 0, 0xff, 0xff, 0, 8, 0, 
                    0, 240, 0, 0, 3, 0, 3, 7, 80
                 };
                this._mSocket.Send(buffer2, 0x19, SocketFlags.None);
                byte[] buffer3 = COTP.TSDU.Read(this._mSocket);
                if ((buffer3 == null) || (buffer3[1] != 3))
                {
                    S7.Net.ErrorCode wrongNumberReceivedBytes = S7.Net.ErrorCode.WrongNumberReceivedBytes;
                    throw new Exception(wrongNumberReceivedBytes.ToString());
                }
                this.MaxPDUSize = (short) ((buffer3[0x12] * 0x100) + buffer3[0x13]);
                return S7.Net.ErrorCode.NoError;
            }
            catch (Exception exception)
            {
                this.LastErrorCode = S7.Net.ErrorCode.ConnectionError;
                this.LastErrorString = string.Format("Couldn't establish the connection to {0}.\nMessage: {1}", this.IP, exception.Message);
                return S7.Net.ErrorCode.ConnectionError;
            }
        }

        private object ParseBytes(VarType varType, byte[] bytes, int varCount, byte bitAdr = 0)
        {

            if (bytes == null || bytes.Length == 0)
                return null;

            switch (varType)
            {
                case VarType.Byte:
                    if (varCount == 1)
                        return bytes[0];
                    else
                        return bytes;
                case VarType.Word:
                    if (varCount == 1)
                        return Word.FromByteArray(bytes);
                    else
                        return Word.ToArray(bytes);
                case VarType.Int:
                    if (varCount == 1)
                        return Int.FromByteArray(bytes);
                    else
                        return Int.ToArray(bytes);
                case VarType.DWord:
                    if (varCount == 1)
                        return DWord.FromByteArray(bytes);
                    else
                        return DWord.ToArray(bytes);
                case VarType.DInt:
                    if (varCount == 1)
                        return DInt.FromByteArray(bytes);
                    else
                        return DInt.ToArray(bytes);
                case VarType.Real:
                    if (varCount == 1)
                        return Types.Real.FromByteArray(bytes);
                    else
                        return Types.Real.ToArray(bytes);
                case VarType.LReal:
                    if (varCount == 1)
                        return Types.LReal.FromByteArray(bytes);
                    else
                        return Types.LReal.ToArray(bytes);

                case VarType.String:
                    return Types.String.FromByteArray(bytes);
                case VarType.S7String:
                    return S7String.FromByteArray(bytes);
                case VarType.S7WString:
                    return S7WString.FromByteArray(bytes);

                case VarType.Timer:
                    if (varCount == 1)
                        return Timer.FromByteArray(bytes);
                    else
                        return Timer.ToArray(bytes);
                case VarType.Counter:
                    if (varCount == 1)
                        return Counter.FromByteArray(bytes);
                    else
                        return Counter.ToArray(bytes);
                case VarType.Bit:
                    if (varCount == 1)
                    {
                        if (bitAdr > 7)
                            return null;
                        else
                            return Bit.FromByte(bytes[0], bitAdr);
                    }
                    else
                    {
                        return Bit.ToBitArray(bytes, varCount);
                    }
                case VarType.DateTime:
                    if (varCount == 1)
                    {
                        return Types.DateTime.FromByteArray(bytes);
                    }
                    else
                    {
                        return Types.DateTime.ToArray(bytes);
                    }
                case VarType.DateTimeLong:
                    if (varCount == 1)
                    {
                        return DateTimeLong.FromByteArray(bytes);
                    }
                    else
                    {
                        return DateTimeLong.ToArray(bytes);
                    }
                default:
                    return null;
            }
        }

        public object Read(string variable)
        {
            var adr = new PLCAddress(variable);
            return Read(adr.DataType, adr.DbNumber, adr.StartByte, adr.VarType, 1, (byte)adr.BitNumber);
        }

        public object Read(DataType dataType, int db, int startByteAdr, VarType varType, int varCount, byte bitAdr = 0)
        {
            int count = this.VarTypeToByteLength(varType, varCount);
            byte[] bytes = this.ReadBytes(dataType, db, startByteAdr, count);
            return this.ParseBytes(varType, bytes, varCount, bitAdr);
        }

        public byte[] ReadBytes(DataType dataType, int db, int startByteAdr, int count)
        {
            int num2;
            List<byte> list = new List<byte>();
            for (int i = startByteAdr; count > 0; i += num2)
            {
                num2 = Math.Min(count, this.MaxPDUSize - 0x12);
                byte[] collection = this.ReadBytesWithASingleRequest(dataType, db, i, num2);
                if (collection == null)
                {
                    return list.ToArray();
                }
                list.AddRange(collection);
                count -= num2;
            }
            return list.ToArray();
        }

        private byte[] ReadBytesWithASingleRequest(DataType dataType, int db, int startByteAdr, int count)
        {
            try
            {
                ByteArray array = new ByteArray(0x1f);
                array.Add(this.ReadHeaderPackage(1));
                array.Add(this.CreateReadDataRequestPackage(dataType, db, startByteAdr, count));
                this._mSocket.Send(array.array, array.array.Length, SocketFlags.None);
                byte[] source = COTP.TSDU.Read(this._mSocket);
                if ((source == null) || (source[14] != 0xff))
                {
                    S7.Net.ErrorCode wrongNumberReceivedBytes = S7.Net.ErrorCode.WrongNumberReceivedBytes;
                    throw new Exception(wrongNumberReceivedBytes.ToString());
                }
                return source.Skip<byte>(0x12).Take<byte>(count).ToArray<byte>();
            }
            catch (SocketException exception)
            {
                this.LastErrorCode = S7.Net.ErrorCode.WriteData;
                this.LastErrorString = exception.Message;
                return null;
            }
            catch (Exception exception2)
            {
                this.LastErrorCode = S7.Net.ErrorCode.WriteData;
                this.LastErrorString = exception2.Message;
                return null;
            }
        }


        public T ReadClass<T>(Func<T> classFactory, int db, int startByteAdr = 0) where T: class
        {
            T sourceClass = classFactory();
            if (this.ReadClass(sourceClass, db, startByteAdr) <= 0)
            {
                return default(T);
            }
            return sourceClass;
        }

        public int ReadClass(object sourceClass, int db, int startByteAdr = 0)
        {
            int classSize = (int)Class.GetClassSize(sourceClass);
            if (classSize <= 0)
            {
                throw new Exception("The size of the class is less than 1 byte and therefore cannot be read");
            }
            byte[] bytes = this.ReadBytes(DataType.DataBlock, db, startByteAdr, classSize);
            Class.FromBytes(sourceClass, bytes);
            return bytes.Length;
        }

        private ByteArray ReadHeaderPackage(int amount = 1)
        {
            ByteArray array = new ByteArray(0x13);
            byte[] items = new byte[3];
            items[0] = 3;
            array.Add(items);
            array.Add((byte) (0x13 + (12 * amount)));
            array.Add(new byte[] { 2, 240, 0x80, 50, 1, 0, 0, 0, 0 });
            array.Add(Word.ToByteArray((ushort) (2 + (amount * 12))));
            byte[] buffer2 = new byte[3];
            buffer2[2] = 4;
            array.Add(buffer2);
            array.Add((byte) amount);
            return array;
        }

        public void ReadMultipleVars(List<DataItem> dataItems)
        {
            if (dataItems.Count > 20)
            {
                throw new Exception("Too many vars requested");
            }
            if (Enumerable.Sum<DataItem>(dataItems, (Func<DataItem, int>) (dataItem => this.VarTypeToByteLength(dataItem.VarType, dataItem.Count))) > 0xde)
            {
                throw new Exception("Too many bytes requested");
            }
            try
            {
                S7.Net.ErrorCode wrongNumberReceivedBytes;
                ByteArray array = new ByteArray(0x13 + (dataItems.Count * 12));
                array.Add(this.ReadHeaderPackage(dataItems.Count));
                foreach (DataItem item in dataItems)
                {
                    array.Add(this.CreateReadDataRequestPackage(item.DataType, item.DB, item.StartByteAdr, this.VarTypeToByteLength(item.VarType, item.Count)));
                }
                this._mSocket.Send(array.array, array.array.Length, SocketFlags.None);
                byte[] source = COTP.TSDU.Read(this._mSocket);
                if ((source == null) || (source[14] != 0xff))
                {
                    wrongNumberReceivedBytes = S7.Net.ErrorCode.WrongNumberReceivedBytes;
                    throw new Exception(wrongNumberReceivedBytes.ToString());
                }
                int index = 14;
                foreach (DataItem item2 in dataItems)
                {
                    if (source[index] != 0xff)
                    {
                        wrongNumberReceivedBytes = S7.Net.ErrorCode.WrongNumberReceivedBytes;
                        throw new Exception(wrongNumberReceivedBytes.ToString());
                    }
                    index += 4;
                    int count = this.VarTypeToByteLength(item2.VarType, item2.Count);
                    item2.Value = this.ParseBytes(item2.VarType, source.Skip<byte>(index).Take<byte>(count).ToArray<byte>(), item2.Count, item2.BitAdr);
                    index += count;
                    if (((item2.Count % 2) != 0) && ((item2.VarType == VarType.Byte) || (item2.VarType == VarType.Bit)))
                    {
                        index++;
                    }
                }
            }
            catch (SocketException exception)
            {
                this.LastErrorCode = S7.Net.ErrorCode.WriteData;
                this.LastErrorString = exception.Message;
            }
            catch (Exception exception2)
            {
                this.LastErrorCode = S7.Net.ErrorCode.WriteData;
                this.LastErrorString = exception2.Message;
            }
        }

        public T? ReadStruct<T>(int db, int startByteAdr = 0) where T: struct
        {
            return (this.ReadStruct(typeof(T), db, startByteAdr) as T?);
        }

        public object ReadStruct(Type structType, int db, int startByteAdr = 0)
        {
            int structSize = Struct.GetStructSize(structType);
            byte[] bytes = this.ReadBytes(DataType.DataBlock, db, startByteAdr, structSize);
            return Struct.FromBytes(structType, bytes);
        }

        private int VarTypeToByteLength(VarType varType, int varCount = 1)
        {
            switch (varType)
            {
                case VarType.Bit:
                    return (varCount + 7) / 8;
                case VarType.Byte:
                    return (varCount < 1) ? 1 : varCount;
                case VarType.String:
                    return varCount;
                case VarType.S7String:
                    return ((varCount + 2) & 1) == 1 ? (varCount + 3) : (varCount + 2);
                case VarType.S7WString:
                    return (varCount * 2) + 4;
                case VarType.Word:
                case VarType.Timer:
                case VarType.Int:
                case VarType.Counter:
                    return varCount * 2;
                case VarType.DWord:
                case VarType.DInt:
                case VarType.Real:
                    return varCount * 4;
                case VarType.LReal:
                case VarType.DateTime:
                    return varCount * 8;
                case VarType.DateTimeLong:
                    return varCount * 12;
                default:
                    return 0;
            }
        }

        public S7.Net.ErrorCode Write(string variable, object value)
        {
            var adr = new PLCAddress(variable);
            return Write(adr.DataType, adr.DbNumber, adr.StartByte, value, adr.BitNumber);
        }

        public S7.Net.ErrorCode Write(DataType dataType, int db, int startByteAdr, object value, int bitAdr = -1)
        {
            if (bitAdr != -1)
            {
                bool flag = false;
                if (value is bool)
                {
                    flag = (bool)value;
                }
                else
                {
                    if (!(value is int))
                    {
                        throw new ArgumentException("Value must be a bool or an int to write a bit", "value");
                    }
                    int num = (int)value;
                    if ((num < 0) || (num > 7))
                    {
                        throw new ArgumentOutOfRangeException(string.Format("Addressing Error: You can only reference bitwise locations 0-7. Address {0} is invalid", bitAdr), "bitAdr");
                    }
                    flag = num == 1;
                }
                return this.WriteBit(dataType, db, startByteAdr, bitAdr, flag);
            }
            else
            {
                return this.WriteBytes(dataType, db, startByteAdr, Protocol.Serialization.SerializeValue(value));
            }
        }

        public S7.Net.ErrorCode WriteBit(DataType dataType, int db, int startByteAdr, int bitAdr, bool value)
        {
            if ((bitAdr < 0) || (bitAdr > 7))
            {
                throw new Exception(string.Format("Addressing Error: You can only reference bitwise locations 0-7. Address {0} is invalid", bitAdr));
            }
            S7.Net.ErrorCode code = this.WriteBitWithASingleRequest(dataType, db, startByteAdr, bitAdr, value);
            if (code != S7.Net.ErrorCode.NoError)
            {
                return code;
            }
            return S7.Net.ErrorCode.NoError;
        }

        public S7.Net.ErrorCode WriteBit(DataType dataType, int db, int startByteAdr, int bitAdr, int value)
        {
            if ((value < 0) || (value > 1))
            {
                throw new ArgumentException("Value must be 0 or 1", "value");
            }
            return this.WriteBit(dataType, db, startByteAdr, bitAdr, value == 1);
        }

        private S7.Net.ErrorCode WriteBitWithASingleRequest(DataType dataType, int db, int startByteAdr, int bitAdr, bool bitValue)
        {
            int length = 0;
            try
            {
                byte[] items = new byte[] { bitValue ? ((byte) 1) : ((byte) 0) };
                length = items.Length;
                int size = 0x23 + items.Length;
                ByteArray array = new ByteArray(size);
                byte[] buffer3 = new byte[3];
                buffer3[0] = 3;
                array.Add(buffer3);
                array.Add((byte) size);
                array.Add(new byte[] { 2, 240, 0x80, 50, 1, 0, 0 });
                array.Add(Word.ToByteArray((ushort) (length - 1)));
                byte[] buffer4 = new byte[2];
                buffer4[1] = 14;
                array.Add(buffer4);
                array.Add(Word.ToByteArray((ushort) (length + 4)));
                array.Add(new byte[] { 5, 1, 0x12, 10, 0x10, 1 });
                array.Add(Word.ToByteArray((ushort) length));
                array.Add(Word.ToByteArray((ushort) db));
                array.Add((byte) dataType);
                int num3 = (int) (((long) (startByteAdr * 8)) / 0xffff);
                array.Add((byte) num3);
                array.Add(Word.ToByteArray((ushort) ((startByteAdr * 8) + bitAdr)));
                byte[] buffer5 = new byte[2];
                buffer5[1] = 3;
                array.Add(buffer5);
                array.Add(Word.ToByteArray((ushort) length));
                array.Add(items);
                this._mSocket.Send(array.array, array.array.Length, SocketFlags.None);
                byte[] buffer2 = COTP.TSDU.Read(this._mSocket);
                if ((buffer2 == null) || (buffer2[14] != 0xff))
                {
                    S7.Net.ErrorCode wrongNumberReceivedBytes = S7.Net.ErrorCode.WrongNumberReceivedBytes;
                    throw new Exception(wrongNumberReceivedBytes.ToString());
                }
                return S7.Net.ErrorCode.NoError;
            }
            catch (Exception exception)
            {
                this.LastErrorCode = S7.Net.ErrorCode.WriteData;
                this.LastErrorString = exception.Message;
                return this.LastErrorCode;
            }
        }

        public S7.Net.ErrorCode WriteBytes(DataType dataType, int db, int startByteAdr, byte[] value)
        {
            int count = 0;
            int length = value.Length;
            while (length > 0)
            {
                int num3 = Math.Min(length, 200);
                S7.Net.ErrorCode code = this.WriteBytesWithASingleRequest(dataType, db, startByteAdr + count, value.Skip<byte>(count).Take<byte>(num3).ToArray<byte>());
                if (code != S7.Net.ErrorCode.NoError)
                {
                    return code;
                }
                length -= num3;
                count += num3;
            }
            return S7.Net.ErrorCode.NoError;
        }

        private S7.Net.ErrorCode WriteBytesWithASingleRequest(DataType dataType, int db, int startByteAdr, byte[] value)
        {
            int length = 0;
            try
            {
                length = value.Length;
                int size = 0x23 + value.Length;
                ByteArray array = new ByteArray(size);
                byte[] items = new byte[3];
                items[0] = 3;
                array.Add(items);
                array.Add((byte) size);
                array.Add(new byte[] { 2, 240, 0x80, 50, 1, 0, 0 });
                array.Add(Word.ToByteArray((ushort) (length - 1)));
                byte[] buffer2 = new byte[2];
                buffer2[1] = 14;
                array.Add(buffer2);
                array.Add(Word.ToByteArray((ushort) (length + 4)));
                array.Add(new byte[] { 5, 1, 0x12, 10, 0x10, 2 });
                array.Add(Word.ToByteArray((ushort) length));
                array.Add(Word.ToByteArray((ushort) db));
                array.Add((byte) dataType);
                int num3 = (int) (((long) (startByteAdr * 8)) / 0xffff);
                array.Add((byte) num3);
                array.Add(Word.ToByteArray((ushort) (startByteAdr * 8)));
                byte[] buffer3 = new byte[2];
                buffer3[1] = 4;
                array.Add(buffer3);
                array.Add(Word.ToByteArray((ushort) (length * 8)));
                array.Add(value);
                this._mSocket.Send(array.array, array.array.Length, SocketFlags.None);
                byte[] buffer = COTP.TSDU.Read(this._mSocket);
                if ((buffer == null) || (buffer[14] != 0xff))
                {
                    S7.Net.ErrorCode wrongNumberReceivedBytes = S7.Net.ErrorCode.WrongNumberReceivedBytes;
                    throw new Exception(wrongNumberReceivedBytes.ToString());
                }
                return S7.Net.ErrorCode.NoError;
            }
            catch (Exception exception)
            {
                this.LastErrorCode = S7.Net.ErrorCode.WriteData;
                this.LastErrorString = exception.Message;
                return this.LastErrorCode;
            }
        }

        public S7.Net.ErrorCode WriteClass(object classValue, int db, int startByteAdr = 0)
        {
            byte[] bytes = new byte[(int)Class.GetClassSize(classValue)];
            Types.Class.ToBytes(classValue, bytes);
            return this.WriteBytes(DataType.DataBlock, db, startByteAdr, bytes);
        }

        public S7.Net.ErrorCode WriteStruct(object structValue, int db, int startByteAdr = 0)
        {
            List<byte> list = Struct.ToBytes(structValue).ToList<byte>();
            return this.WriteBytes(DataType.DataBlock, db, startByteAdr, list.ToArray());
        }

        public CpuType CPU { get; private set; }

        public string IP { get; private set; }

        public bool IsAvailable
        {
            get
            {
                using (Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
                {
                    return (this.Connect(socket) == S7.Net.ErrorCode.NoError);
                }
            }
        }

        public bool IsConnected
        {
            get
            {
                try
                {
                    if (this._mSocket == null)
                    {
                        return false;
                    }
                    return ((!this._mSocket.Poll(0x3e8, SelectMode.SelectRead) || (this._mSocket.Available != 0)) && this._mSocket.Connected);
                }
                catch
                {
                    return false;
                }
            }
        }

        public S7.Net.ErrorCode LastErrorCode { get; private set; }

        public string LastErrorString { get; private set; }


        public short MaxPDUSize { get; set; }

        public short Rack { get; private set; }

        public short Slot { get; private set; }

        public string CpuHotStart()
        {
            if ((this._mSocket != null) && this._mSocket.Connected)
            {
                int _ret = this._mSocket.Send(this.S7_HOT_START);
                return "IMK: result " + _ret;
            }
            else
            {
                return "IMK: 连接断开 ";
            }
        }

        /// <summary>
        /// 对PLC进行冷启动，目前仅适用于200smart型号<br />
        /// Cold start for PLC, currently only applicable to 200smart model
        /// </summary>
        /// <returns>是否启动成功的结果对象</returns>
        public string CpuColdStart()
        {
            if ((this._mSocket != null) && this._mSocket.Connected)
            {
                int _ret = this._mSocket.Send(this.S7_COLD_START);
                return "IMK: result " + _ret;
            }
            else
            {
                return "IMK: 连接断开 ";
            }
        }

        /// <summary>
        /// 对PLC进行停止，目前仅适用于200smart型号<br />
        /// Stop the PLC, currently only applicable to the 200smart model
        /// </summary>
        /// <returns>是否启动成功的结果对象</returns>
        public string CpuStop()
        {
            if ((this._mSocket != null) && this._mSocket.Connected)
            {
                int _ret = this._mSocket.Send(this.S7_STOP);
                return "IMK: result " + _ret;
            }
            else
            {
                return "IMK: 连接断开 ";
            }
        }
    }
}

